package org.ruoyi.ppt.service;

import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.baomidou.mybatisplus.core.toolkit.Wrappers;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.StringUtils;
import com.alibaba.fastjson2.JSON;
import com.alibaba.fastjson2.JSONObject;
import org.jetbrains.annotations.Contract;
import org.jetbrains.annotations.NotNull;
import org.ruoyi.common.core.exception.ServiceException;
import org.ruoyi.common.core.utils.MapstructUtils;
import org.ruoyi.common.oss.core.OssClient;
import org.ruoyi.common.oss.entity.UploadResult;
import org.ruoyi.common.oss.enumd.AccessPolicyType;
import org.ruoyi.common.oss.factory.OssFactory;
import org.ruoyi.ppt.domain.PptResult;
import org.ruoyi.ppt.domain.PptWithImages;
import org.ruoyi.ppt.domain.SysSourcematerial;
import org.ruoyi.ppt.domain.vo.PptStatusVo;
import org.ruoyi.ppt.domain.vo.SysSourcematerialVo;
import org.ruoyi.ppt.mapper.PptResultMapper;
import org.ruoyi.ppt.mapper.SysSourcematerialMapper;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.scheduling.annotation.Async;
import org.springframework.stereotype.Service;
import org.springframework.http.HttpEntity;
import org.springframework.http.HttpHeaders;
import org.springframework.http.HttpMethod;
import org.springframework.http.ResponseEntity;
import org.springframework.web.client.RestTemplate;
import org.springframework.web.multipart.MultipartFile;
import java.io.IOException;
import java.net.URI;
import java.net.http.HttpClient;
import java.net.http.HttpRequest;
import java.net.http.HttpResponse;
import java.time.Duration;
import java.util.List;
import java.util.concurrent.Executors;

@Slf4j
@RequiredArgsConstructor
@Service
public class PptService {
    // PPT文档生成接口地址
    private static final String API_URL_CREATE = "https://saas.api.yoo-ai.com/apps/ppt-create";
    // 获取生成PPT结果接口地址
    private static final String API_URL_RESULT = "https://saas.api.yoo-ai.com/apps/ppt-result";
    //PPT编辑器
    private static final String API_URL_EDITOR = "https://saas.api.yoo-ai.com/apps/ppt-editor";
    //大纲生成接口地址
    private static final String API_URL_STRUCTURE = "https://saas.api.yoo-ai.com/apps/ppt-structure";
    //pptAI模板生成方案接口地址
    private static final String API_URL_COVER = "https://saas.api.yoo-ai.com/apps/ppt-cover";
    //ppt下载
    private static final String API_URL_DOWNLOAD = "https://saas.api.yoo-ai.com/apps/ppt-download";
    //文件生成PPT接口地址
    private static final String API_URL_CREATE_FILE = "https://saas.api.yoo-ai.com/apps/ppt-create-file";

    @Value("${yoo.ai.api-key}")
    private String API_KEY;

// 在创建 HttpClient 时添加更健壮的配置
    HttpClient client = HttpClient.newBuilder()
        .version(HttpClient.Version.HTTP_2)
        .connectTimeout(Duration.ofSeconds(30))
        .followRedirects(HttpClient.Redirect.NORMAL)
        // 重要：设置重试策略
        .executor(Executors.newCachedThreadPool())
        .build();

    private final SysSourcematerialMapper baseMapper;

    private final PptResultMapper pptResultMapper;

    private final PptImportService pptImportService;


    public Object getPptCreate(String requestBody) {
        String result = sendPostRequest(API_URL_CREATE, requestBody);
        return JSON.parseObject(result).get("data");
    }

    public Object getPptResult(String id) {
//        String result = sendGetRequest(API_URL_RESULT + "?id=" + id);
        String result = waitUntilDone(id);
//        pptImportService.importFromJson(result);
        pptImportService.importFromJsonBatch(result); // 使用批量入库
        return JSON.parseObject(result).get("data");
    }

    public Object getPptEditor(String requestBody) {
        String result = sendPostRequest(API_URL_EDITOR, requestBody);
        return JSON.parseObject(result).get("data");
    }

    public Object getPptStructure(String requestBody) {
        String result = sendPostRequest(API_URL_STRUCTURE, requestBody);
        return JSON.parseObject(result).get("data");
    }

    public Object getPptCover(String requestBody) {
        String result = sendPostRequest(API_URL_COVER, requestBody);
        return JSON.parseObject(result).get("data");
    }

    public Object getPptDownload(String id) {
        String result = sendGetRequest(API_URL_DOWNLOAD + "?id=" + id);
//        String download_url = "http://127.0.0.1:9000/harry/2025/09/18/0i2guz0xzgz3y.pptx";
//        uploadFromUrl(download_url, "download.pptx");
        return JSON.parseObject(result).get("data");
    }

    public Object getPptStructureCustom(String requestBody) {
        String result = sendPostRequest(API_URL_CREATE, requestBody);
        return JSON.parseObject(result).get("data");
    }

    public Object getPptCreateFile(MultipartFile file, String name) {
        String originalfileName = file.getOriginalFilename();
        String suffix = StringUtils.substring(originalfileName, originalfileName.lastIndexOf("."),
                originalfileName.length());
        OssClient storage = OssFactory.instance();
        UploadResult uploadResult;
        try {
            uploadResult = storage.uploadSuffix(file.getBytes(), suffix, file.getContentType());
        } catch (IOException e) {
            throw new ServiceException(e.getMessage());
        }
        String url = uploadResult.getUrl();
        JSONObject requestBody = new JSONObject();
        requestBody.put("file_url", url);
        requestBody.put("user_name", name);
        String result = sendPostRequest(API_URL_CREATE_FILE, requestBody.toString());
        return JSON.parseObject(result).get("data");
//        return url;
    }

    public Object getPptStructureMarkdown(String requestBody) {
        String result = sendPostRequest(API_URL_CREATE, requestBody);
        return JSON.parseObject(result).get("data");
    }

    private String sendPostRequest(String url, String requestBody) {
        HttpRequest request = HttpRequest.newBuilder()
                .uri(URI.create(url))
                .header("Authorization", API_KEY)
                .header("Content-Type", "application/json")
                .POST(HttpRequest.BodyPublishers.ofString(requestBody))
                .build();

        return sendRequest(request);
    }

    private String sendGetRequest(String url) {
        HttpRequest request = HttpRequest.newBuilder()
                .uri(URI.create(url))
                .header("Authorization", API_KEY)
                .GET()
                .build();

        return sendRequest(request);
    }

    private String sendRequest(HttpRequest request) {
        try {
            HttpResponse<String> response = client.send(request, HttpResponse.BodyHandlers.ofString());
            System.out.println("Status code: " + response.statusCode());
            System.out.println("Response body: " + response.body());
            return response.body();
        } catch (IOException e) {
            handleException(e);
        } catch (InterruptedException e) {
            Thread.currentThread().interrupt();
            handleException(e);
        }
        return null;
    }

    // 文件上传
    public SysSourcematerialVo uploadFromUrl(String fileUrl, String originalFileName) {
        try {
            // 1. 从URL下载文件
            RestTemplate restTemplate = new RestTemplate();
            HttpHeaders headers = new HttpHeaders();
            HttpEntity<String> entity = new HttpEntity<>(headers);

            ResponseEntity<byte[]> response = restTemplate.exchange(
                    fileUrl,
                    HttpMethod.GET,
                    entity,
                    byte[].class
            );

            byte[] fileBytes = response.getBody();

            // 2. 获取文件后缀和Content-Type
            String suffix = StringUtils.substring(originalFileName, originalFileName.lastIndexOf("."),
                    originalFileName.length());

            // 3. 上传到Minio
            OssClient storage = OssFactory.instance();
            UploadResult uploadResult = storage.uploadSuffix(fileBytes, suffix, getContentType(suffix));

            // 4. 保存文件信息到数据库
            SysSourcematerial oss = new SysSourcematerial();
            oss.setUrl(uploadResult.getUrl());
            oss.setFileSuffix(suffix);
            oss.setFileName(uploadResult.getFilename());
            oss.setOriginalName(originalFileName);
            oss.setService(storage.getConfigKey());
            baseMapper.insert(oss);

            SysSourcematerialVo sysSourcematerialVo = MapstructUtils.convert(oss, SysSourcematerialVo.class);
            return this.matchingUrl(sysSourcematerialVo);

        } catch (Exception e) {
            throw new ServiceException("文件下载或上传失败: " + e.getMessage());
        }
    }

    private void handleException(Exception e) {
        e.printStackTrace();
        System.out.println("An exception occurred: " + e.getMessage());
    }

    /**
     * 匹配Url
     *
     * @param oss OSS对象
     * @return oss 匹配Url的OSS对象
     */
    private SysSourcematerialVo matchingUrl(SysSourcematerialVo oss) {
        OssClient storage = OssFactory.instance(oss.getService());
        // 仅修改桶类型为 private 的URL，临时URL时长为120s
        if (AccessPolicyType.PRIVATE == storage.getAccessPolicy()) {
            oss.setUrl(storage.getPrivateUrl(oss.getFileName(), 120));
        }
        return oss;
    }

    public PptWithImages getPptImages(String id) {
        return pptResultMapper.selectPptWithImages(id);
    }

    public List<PptResult> getPptList() {
        QueryWrapper<PptResult> wrapper = Wrappers.<PptResult>query()
                .orderByDesc("created_at");   // 降序；想升序用 orderByAsc
        return pptResultMapper.selectList(wrapper);
    }


        // 根据文件后缀获取Content-Type
    @Contract(pure = true)
    private @NotNull String getContentType(String suffix) {
        switch (suffix.toLowerCase()) {
            case ".pptx":
                return "application/vnd.openxmlformats-officedocument.presentationml.presentation";
            case ".ppt":
                return "application/vnd.ms-powerpoint";
            case ".docx":
                return "application/vnd.openxmlformats-officedocument.wordprocessingml.document";
            case ".doc":
                return "application/msword";
            case ".xlsx":
                return "application/vnd.openxmlformats-officedocument.spreadsheetml.sheet";
            case ".xls":
                return "application/vnd.ms-excel";
            case ".pdf":
                return "application/pdf";
            case ".jpg":
            case ".jpeg":
                return "image/jpeg";
            case ".png":
                return "image/png";
            case ".gif":
                return "image/gif";
            default:
                return "application/octet-stream";
        }
    }

    /**
     * 检查输入字符串是否包含指定关键字（不区分大小写）
     * @param input 输入字符串
     * @param keywords 关键字列表
     * @return 包含任意关键字返回true，否则false
     */
    public static boolean containsKeyword(String input, List<String> keywords) {
        // 参数校验
        if (input == null || keywords == null || keywords.isEmpty()) {
            return false;
        }

        // 统一转为小写进行不区分大小写比较
        String lowerInput = input.toLowerCase();

        for (String keyword : keywords) {
            if (keyword == null) continue;
            // 中文关键字大小写转换不影响，英文关键字会统一处理
            String lowerKeyword = keyword.toLowerCase();

            if (lowerInput.contains(lowerKeyword)) {
                return true;
            }
        }
        return false;
    }

    /**
     * 轮询第三方接口直到 PPT 处理结束（成功 or 失败）
     * 每轮都把当前状态/进度通过 WebSocket 推给前端
     *
     * @param pptId 任务编号
     * @return 第三方返回的完整 JSON 字符串（含下载地址）
     * @throws RuntimeException 轮询 24 次仍未结束则抛异常
     */
    public String waitUntilDone(String pptId) {
        int maxAttempt = 120;          // 24 * 5s = 120s
        int attempt = 0;

        while (attempt++ < maxAttempt) {
            // 1. 请求第三方
            String resp = sendGetRequest(API_URL_RESULT + "?id=" + pptId);
            JSONObject root   = JSON.parseObject(resp);
            JSONObject data   = root.getJSONObject("data");

            int    status   = data.getIntValue("status");
            int    progress = data.getIntValue("progress");   // 0~100
            String preview  = data.getString("preview_url");
            String message  = data.getString("message");

            // 2. 每次轮询都推送一次，保证前端实时看到进度
            PptStatusVo vo = new PptStatusVo(
                    pptId,
                    status,          // 1 处理中  2 完成  3 失败
                    progress,
                    preview,
                    status == 3 ? message : "处理中");
            PptWebSocketServer.sendMessage(pptId, vo);   // <-- 关键

            // 3. 结束条件
            if (status == 2) {          // 成功
                log.info("PPT {} 已完成，共轮询 {} 次", pptId, attempt);
                return resp;            // 整条 JSON 给上层解析入库
            }
            if (status == 3) {          // 失败
                log.warn("PPT {} 处理失败，错误信息：{}", pptId, message);
                throw new RuntimeException("PPT " + pptId + " 处理失败：" + message);
            }

            // 4. 继续等待
            log.debug("PPT {} 处理中，第 {} 次重试，progress={}", pptId, attempt, progress);
            try {
                Thread.sleep(1_000);
            } catch (InterruptedException e) {
                Thread.currentThread().interrupt();   // 恢复中断标记
                throw new RuntimeException("轮询线程被中断", e);
            }
        }

        // 5. 超时
        throw new RuntimeException("PPT " + pptId + " 轮询超时（120s）");
    }

    @Async("pptAsyncPool")
    public void pullAndSave(String pptId) {
        /* 1. 先推一条“排队/开始处理”的占位消息，progress=0，避免前端空白 */
        PptWebSocketServer.sendMessage(
                pptId,
                new PptStatusVo(pptId, 1, 0, null, "开始处理"));

        try {
            // 2. 阻塞轮询，直到第三方完成（内部每 5s 会推一次进度）
            String json = waitUntilDone(pptId);

            // 3. 落库
            pptImportService.importFromJsonBatch(json);

            // 4. 推“完成”消息（带 100% 与预览地址）
            JSONObject data = JSON.parseObject(json).getJSONObject("data");
            // 成功终态
            PptStatusVo doneVo = new PptStatusVo(pptId, 2, 100,
                    data.getString("preview_url"), "完成");
            PptWebSocketServer.sendMessageAndClose(pptId, doneVo);

        } catch (Exception e) {
            log.error("异步拉取PPT失败,id={}", pptId, e);
            // 5. 失败也推一条，方便前端弹窗/提示
            PptWebSocketServer.sendMessageAndClose(pptId,
                    new PptStatusVo(pptId, 3, 0, null, "处理失败：" + e.getMessage()));
        }
    }


}
